package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.AlertDataMapper;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.nursing.websocker.WebSocketServer;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 规则生成Service业务层处理
 *
 * @author ruoyi
 * @date 2025-09-22
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService {
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    //设备维修人员的名字
    @Value("${alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;

    //管理员的名字
    @Value("${alert.managerRole}")
    private String managerRole;

    @Autowired
    private IAlertDataService alertDataService;

    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 查询规则生成
     *
     * @param id 规则生成主键
     * @return 规则生成
     */
    @Override
    public AlertRule selectAlertRuleById(Long id) {
        return getById(id);
    }

    /**
     * 查询规则生成列表
     *
     * @param alertRule 规则生成
     * @return 规则生成
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule) {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增规则生成
     *
     * @param alertRule 规则生成
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule) {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改规则生成
     *
     * @param alertRule 规则生成
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule) {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除规则生成
     *
     * @param ids 需要删除的规则生成主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids) {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除规则生成信息
     *
     * @param id 规则生成主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id) {
        return removeById(id) ? 1 : 0;
    }

    @Override
    public void alertFilter() {
        //1.查询所有启用的规则，如果为空，直接结束
        Long count = alertRuleMapper.selectCount(new LambdaQueryWrapper<AlertRule>().eq(AlertRule::getStatus, 1));
        if (count <= 0) {
            return;
        }
        //2.查询所有设备上报的数据，如果为空，直接结束
        List<Object> deviceLastDataList = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (ObjectUtil.isEmpty(deviceLastDataList)) {
            return;
        }

        //3.格式化查询的数据'(理解不透彻）
        //item = List<DeviceData>
        List<DeviceData> waitCheckDeviceDataList = new ArrayList<>();
        //把所有的设备数据都添加到一个集合中
        deviceLastDataList.forEach(item -> {
            if (ObjectUtil.isNotEmpty(item)) {
                waitCheckDeviceDataList.addAll((List<DeviceData>) item);
            }
        });
        //如果数据为空，直接结束
        if (ObjectUtil.isEmpty(waitCheckDeviceDataList)) {
            return;
        }

        //4.遍历每一条规则，判断是否出发警告
        waitCheckDeviceDataList.forEach(deviceData -> {
            alramRuleCheck(deviceData);
        });
    }

    /**
     * //4.遍历每一条规则，判断是否出发警告
     *
     * @param deviceData
     */
    private void alramRuleCheck(DeviceData deviceData) {
        //1.如果设备数据为空，直接结束
        if (ObjectUtil.isEmpty(deviceData)) {
            return;
        }
        //1.2,判断数据距离上报时间是否超过1分钟，如果超过了，说明设备没有上报数据了，则结束
        LocalDateTime alarmTime = deviceData.getAlarmTime();
        long betweenTime = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if (betweenTime > 60) {
            return;
        }
        //2.查询针对该产品所有启用的通用规则
        List<AlertRule> commonRuleList = alertRuleMapper
                .selectList(new LambdaQueryWrapper<AlertRule>()
                        .eq(AlertRule::getProductKey, deviceData.getProductKey())
                        .eq(AlertRule::getIotId, "-1")
                        .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                        .eq(AlertRule::getStatus, 1));


        //3.查询设备数据对应的规则
        List<AlertRule> deviceRuleList = alertRuleMapper
                .selectList(new LambdaQueryWrapper<AlertRule>()
                        .eq(AlertRule::getProductKey, deviceData.getProductKey())
                        .eq(AlertRule::getIotId, deviceData.getIotId())
                        .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                        .eq(AlertRule::getStatus, 1));
        //3.1 合并规则
        List<AlertRule> alertRuleList = (List<AlertRule>)CollUtil.addAll(commonRuleList, deviceRuleList);


        //3.2 判断所有该设备的所有规则是否为空，如果为空，则结束，遍历下一条数据
        if (ObjectUtil.isEmpty(alertRuleList)) {
            return;
        }

        //4.遍历每一条规则
        alertRuleList.forEach(alertRule -> {
            deviceDataRuleCheck(alertRule,deviceData);
        });
    }

    //4.遍历每一条规则
    private void deviceDataRuleCheck(AlertRule alertRule,DeviceData deviceData) {

        //5.判断是否在规则的有效时间之类
        //5.1,格式化开始时间和结束时间
        String[] split = alertRule.getAlertEffectivePeriod().split("~");
        LocalTime ruleStartTime = LocalTime.parse(split[0]);
        LocalTime ruleEndTime = LocalTime.parse(split[1]);
        //5.2,判断时间是否在规则有效时间之内，注意，这里使用的是alarmTime，而不是LocalDateTime.now()
        //5.3,如果时间不在规则有效时间之内，则结束，遍历下条数据
        LocalTime alarmTime = deviceData.getAlarmTime().toLocalTime();
        if(alarmTime.isBefore(ruleStartTime) || alarmTime.isAfter(ruleEndTime)){
            return;
        }

        //6.校验数据是否达到阈值，未到达，删除redis中的报警数
        String dataValue = deviceData.getDataValue();
        String operator = alertRule.getOperator();
        int compare = NumberUtil.compare(Double.parseDouble(dataValue), alertRule.getValue());

        //组装报警数的 key
        String alarmKey  = CacheConstants.IOT_COUNT_ALERT + deviceData.getIotId()+deviceData.getFunctionId()+alertRule.getId();

        if((">=".equals(operator) && compare >= 0)  || ("<".equals(operator) && compare < 0) ){
            //触发了报警规则，继续执行
            log.warn(deviceData.getIotId()+"触发了报警规则,当且报警次数为:"+ redisTemplate.opsForValue().get(alarmKey));
        }else {
            //没有达到预期，删除redis中的报警数，并退出
            log.info(deviceData.getIotId()+"：没有达到预期，删除了报警数");
            redisTemplate.delete(alarmKey);
            return;
        }

        //7.如果达到了，判断是否在报警沉默期之内
        //7.1组装沉默周期
        String silenceKey = CacheConstants.IOT_SILENT_ALERT + deviceData.getIotId()+deviceData.getFunctionId()+alertRule.getId();

        //8.在沉默周期内则无操作
        if(redisTemplate.hasKey(silenceKey)){
            log.info(deviceData.getIotId()+"：在沉默周期内，无操作");
            return;
        }

        //9.如果在沉默周期外，则报警去加1，并判断是否达到报警的周期
        //新方法，值新增后直接返回
        Long incrementCount =  redisTemplate.opsForValue().increment(alarmKey);

        //查询持续周期
        Integer duration = alertRule.getDuration();
        if(incrementCount >= duration){
            //触发了报警规则，继续向下执行
            log.warn(deviceData.getIotId()+"要开始报警了");
        }else {
            //10.没达到报警数，则结束，遍历下条数据
            return;
        }

        //查询沉默周期
        Integer alertSilentPeriod = alertRule.getAlertSilentPeriod();

        //11.如果达到报警周期，则发送报警信息，且删除redis中的报警次数，添加沉默周期
        redisTemplate.delete(alarmKey);
        redisTemplate.opsForValue().set(silenceKey,"1",alertSilentPeriod, TimeUnit.MINUTES);

        //12.报警，判断是老人的数据，还是设备数据
        //alertRule.getAlertDataType() == 0 →老人异常数据，1 →设备异常数据

        //组装通知人员的id
       List<Long> userIdList =null;
        if (alertRule.getAlertDataType() == 0){
            //这是老人异常数据
            //deviceData.getLocationType()→ 0：随身设备 1：固定设备
            if (deviceData.getLocationType() == 0){
                //这是老人身上的设备的数据
                //通过老人找到到对应护理人员的id
                userIdList = deviceMapper.selectNursingIdByIotIdWithElder(deviceData.getIotId());
            }else {
                //这是老人所在床位的设备的数据
                //通过床位找到到对应老人，然后找到对应的护理人员
                userIdList = deviceMapper.selectNursingIdByIotIdWithBed(deviceData.getIotId());
            }
        }else {
            //这是设备异常数据
            //找到维修工的id
            userIdList = sysUserRoleMapper.selectByRoleName(deviceMaintainerRole);
        }

        //15，获取管理员的id，并组装id
        List<Long> managerIdList = sysUserRoleMapper.selectByRoleName(managerRole);

        //组装报警信息的id并去重
        ArrayList<Long> needNotifyUserIds = CollUtil.distinct(CollUtil.addAll(userIdList, managerIdList));

        //16.发送报警信息
        log.error("需要通知人有:{}",needNotifyUserIds);

        //保存报警数据到mysql-AlertData表
        AlertData alertData = saveBatchAlertData(alertRule, deviceData, needNotifyUserIds);

        //调用webSocket服务，发送消息给前端
        sendMessageToConsumer(alertRule, alertData, needNotifyUserIds);

    }

    //调用webSocket服务，发送消息给前端
    private void sendMessageToConsumer(AlertRule alertRule, AlertData alertData, ArrayList<Long> needNotifyUserIds) {
        //拷贝数据
        AlertNotifyVo alertNotifyVo = BeanUtil.toBean(alertData, AlertNotifyVo.class);
        //多余的补全
        alertNotifyVo.setFunctionName(alertRule.getFunctionName());
        alertNotifyVo.setAlertDataType(alertRule.getAlertDataType());
        alertNotifyVo.setNotifyType(1);
        //其余无序设计

        webSocketServer.sendMessageToConsumer(alertNotifyVo, needNotifyUserIds);
    }

    //保存报警数据到mysql-AlertData表
    private AlertData saveBatchAlertData(AlertRule alertRule, DeviceData deviceData, ArrayList<Long> needNotifyUserIds) {
        //属性拷贝
        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
        //属性补全
        //报警规则id
        alertData.setAlertRuleId(alertRule.getId());
        //状态 0 待处理 1 已处理
        alertData.setStatus(0);
        //报警数据类型，0：老人异常数据，1：设备异常数据
        alertData.setType(alertRule.getAlertDataType());
        //报警愿原因 报警原因，格式：功能名称+运算符+阈值+持续周期+聚合周期
        //！！！取消主键
        alertData.setId(null);
        //构建格式
        String alertReason = alertRule.getFunctionName() + alertRule.getOperator() + alertRule.getValue() + alertRule.getDuration() + alertRule.getAlertSilentPeriod();
        alertData.setAlertReason(alertReason);

        // 3、循环待通知人ID集合，组装 AlertData 集合
        List<AlertData> alertDataList = needNotifyUserIds.stream().map(userId -> {
            return BeanUtil.toBean(alertData, AlertData.class)
                    .setUserId(userId);
        }).collect(Collectors.toList());
        //存入数据库
        alertDataService.saveBatch(alertDataList);
        return alertData;
    }
}
